package com.monlyu.util;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.logging.Log;

import com.monlyu.config.AppArgs;

/**
 * 对类的一些操作
 * 
 * @author monlyu
 */
public class ClassUtil {

	final static Log log = LogUtil.log(ClassUtil.class);

	/**
	 * 获取setter方法名<br>
	 * <code>inPut=>id</code> <code>outPut=>getId</code>
	 */
	public static String getter(String field) {
		return "get" + StringUtil.capFisrtChar(field);
	}

	public static String bolGetter(String field) {
		return "is" + StringUtil.capFisrtChar(field);
	}

	/**
	 * 获取set的方法名
	 */
	public static String setter(String field) {
		return "get" + StringUtil.capFisrtChar(field);
	}

	/**
	 * 根据字符串找到某个类中某个类型<br>
	 * 如果没有 则返回<code>null</code>
	 * 
	 * @since 2009-12-28
	 */
	public static Class<?> getFiledType(Object object, String field) {
		Class<?> cls = null;
		try {
			cls = object.getClass().getMethod(ClassUtil.getter(field)).getReturnType();
		} catch (Exception e) {
			try {
				cls = object.getClass().getMethod(ClassUtil.bolGetter(field)).getReturnType();
			} catch (Exception e1) {
				LogUtil.log(ClassUtil.class, e1).error("查找字段类型失败:" + e);
			}
		}
		return cls;
	}

	/**
	 * 是否为默认包
	 * 
	 * @since 2009-12-28
	 */
	public static boolean isInDefVoPackage(Class<?> clazz) {
		return isInPackage(clazz, AppArgs.VO_PACKAGE);
	}

	/**
	 * 判断类 clazz 是否在 package 中
	 * 
	 * @since 2009-12-30
	 */
	public static boolean isInPackage(Class<?> clazz, String packname) {
		return clazz.getPackage().getName().equals(packname);
	}

	/**
	 * 获取某个类
	 * 
	 * @since 2010-1-1
	 */
	public static Class<?> getClass(Object inObj) {
		final Class<?> clazz = inObj.getClass();
		return clazz.getName().indexOf("$") > 0 ? clazz.getSuperclass() : clazz;
	}

	/**
	 * 获取某个包下面的所有类
	 * 
	 * @since 2010-1-8
	 * 
	 * @param packname
	 *            包名 如：com.monlyu.vo
	 * @param recursive
	 *            是否进行迭代
	 * @return 某个包内的所有类的实例
	 */
	public static Set<Class<?>> getClassesByPackage(String packname, boolean recursive) {
		if (StringUtil.isEmpty(packname)) {
			return null;
		}
		Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
		Enumeration<URL> dirs;
		try {
			String packdir = packname.replace('.', '/');
			dirs = Thread.currentThread().getContextClassLoader().getResources(packdir);
			while (dirs.hasMoreElements()) {
				URL url = dirs.nextElement();
				if (url.getProtocol().equals("file")) {
					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
					addFileClass2Set(packname, filePath, recursive, classes);
				} else if (url.getProtocol().equals("jar")) {
					JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
					addJarClass2Set(packname, recursive, classes, packdir, jar);
				}
			}
		} catch (Exception e) {
			log.error("获取类列表的时候出错" + e);
		}
		return classes;
	}

	/**
	 * 把jar包里面还有你需要的目录添加到你的的set中去
	 * 
	 * @param packname
	 *            包名
	 * @param recursive
	 *            是否进行迭代
	 * @param classes
	 *            输入输出的类
	 * @param packdir
	 *            包的目录`
	 * @param jar
	 *            jar文件
	 * @return
	 */
	private static void addJarClass2Set(String packname, boolean recursive, Set<Class<?>> classes, String packdir,
			JarFile jar) {
		while (jar.entries().hasMoreElements()) {
			JarEntry jarEntry = jar.entries().nextElement();
			String name = jarEntry.getName();
			if (name.charAt(0) == '/') {
				name = name.substring(1);
			}
			if (name.startsWith(packdir)) {
				int idx = name.lastIndexOf('/');
				if (idx == -1) {
					packname = name.substring(0, idx).replace('/', '.');
				}
				if (idx != -1 || recursive) {
					if (name.endsWith(".class") && !jarEntry.isDirectory()) {
						String className = name.substring(packname.length() + 1, name.length() - 6);
						try {
							classes.add(Class.forName(packname + '.' + className));
						} catch (ClassNotFoundException e) {
							log.warn("找不到:" + className);
						}
					}
				}
			}
		}
	}

	/**
	 * 添加文件Class到Set里面去 添加磁盘上单独的class文件进入Set列表
	 */
	private static void addFileClass2Set(String packname, String packPath, final boolean recursive,
			Set<Class<?>> classes) {
		File dir = new File(packPath);
		if (!dir.exists() || !dir.isDirectory()) {
			log.error("该包" + packname + "下无任何文件");
			return;
		}
		File[] files = dir.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
			}
		});
		for (File file : files) {
			if (file.isDirectory()) {
				addFileClass2Set(packname + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
			} else {
				String className = file.getName().substring(0, file.getName().length() - 6);
				try {
					classes.add(Class.forName(packname + '.' + className));
				} catch (ClassNotFoundException e) {
					log.warn("类文件尚未找到" + className);
				}
			}
		}
	}

	public static Set<Class<?>> getClassWithAnnoByPackage(Class<? extends Annotation> clazz, String packname) {
		Set<Class<?>> set = new LinkedHashSet<Class<?>>();
		for (Class<?> classinSet : getClassesByPackage(packname, false)) {
			if (classinSet.getAnnotation(clazz) != null) {
				classinSet.getName();
				set.add(classinSet);
			}
		}
		return set;
	}
}